¡Hola! Soy Ronny Hdez-Mora y he hecho una recopilación de información sobre cómo hacer uso en R de datos que representan fechas y horas. La intención es hacerle un poco más fácil la vida a quién siga este tutorial y necesite una introducción sobre el manejo de este tipo de datos.
P.D. Cualquier duda, comentario, corrección que quieran aportar, por favor me lo hacen saber a mi correo ronny.hernandezm@gmail.com
Mención de lo principal del tutorial:
Las fechas y horas aunque nos parezca algo muy común, son datos que tienen su particularidad. Si tomamos en cuenta la manera en que medimos el tiempo y cómo lo adaptamos a nuestro diario vivir podríamos darnos cuenta de que no todos los meses tienen la misma cantidad de días, no en todos los países o regiones escribimos las fechas bajo el mismo formato, tenemos años bisiestos, si nos vamos 200 millones de años hacia atrás los días tenían 23 horas y un año tenía 380 días, entre otras curiosidades del tiempo.
Si esto es así, ¿cómo podemos trabajar datos que vienen con diferentes formatos, con unidades que pueden cambiar en cuanto a lo que representan?, ¿cómo podemos hacer que nuestra computadora entienda que los datos que le estamos dando son fechas y horas y no otro tipo de valor?.
Pues bien, aunque suene algo complicado, no lo es y con R encontramos un sistema robusto para poder lidiar con estas situaciones.
R tiene diversas maneras de representar objetos en el sistema. Más allá de los dobles, enteros, carácteres, lógicos y complejos, R tiene una clase especial para representar las fechas y horas.
Si queremos revisar cuál es la fecha y hora que tenemos en nuestro sistema podemos hacerlo con la función Sys.time()
Sys.time()
## [1] "2018-03-28 16:59:31 CST"
Lo que nos muestra es una descripción de la fecha y hora de manera meramente jerárquica: la escala más grande (años) va primero, seguido por el mes y por último el día, separados entre sí por un guión; luego hay un espacio en blanco y bajo la misma lógica tenemos la hora, los minutos y los seugundos, separados cada uno por dos punto. Al final tenemos un dato que nos indica el sistema de tiempo utilizado, en este caso Central Standard Time.
Esta convención es buena para poder leer de manera fácil, sin embargo, para la realización de cálculos esto es un poco difícil por lo que el manejo de estas a lo “interno” de R se hace basado en segundos.
Para revisar su tipo de dato y su clase vamos a guardar esta fecha y hora de nuestro ordenador en un objeto de la siguiente manera:
fecha_hora <- Sys.time() # Guardar en un objeto
typeof(fecha_hora) # Tipo de dato
## [1] "double"
class(fecha_hora) # Clase del objeto
## [1] "POSIXct" "POSIXt"
Al momento de utilizar la funciónSys.time(), nos devuelve una respuesta que a primera instancia nos parecería un objeto de tipo character por sus comillas alrededor de esto, sin embargo, al revisar podemos notar que es un objeto de tipo double y su clase es _ “POSIXct” “POSIXt” _ (tiene dos clases)
Como mencionamos anteriormente, por convención en R se utiliza una representación numérica, basada en segundos. Esto quiere decir que cada fecha se representa por el número de segundos que han transcurrido desde las 12:00 A.M. del 1 Enero de 1970. (UTC: Coordinated Universal Time )
Esto es bastante útil si queremos generar gráficos de series de tiempo, pero qué sucede si queremos representar las medias por mes, o por día. En este caso es necesario contar con dos maneras de representar las fechas:
POSIXct el sufijo ct se refiere a continuos time y este formato es representado de manera númerica por segundos. Este es un vector que se puede usar como una variable continua en modelos de regresión.
POSIXlt el sufijo lt corresponde a list time y lo que hace es una lista de todas las descripciones categóricas del tiempo. Es muy útil como variable explicativa categórica.
Para revisar de qué manera R hace esto podemos hacer uso de la función unclass()
unclass(fecha_hora)
## [1] 1522277972
El resultado que obtenemos es un elemento que R utiliza para construir un vector doble. Lo que tenemos allí es que desde las 12:00 A.M. del 1 de Enero de 1970 han transcurrido 1 514 244 708 hasta el día en que este tutorial se ha creado y este es el formato POSIXct
En el caso de caso de una lista como POSIXlt vamos a revisar los componentes del mismo objeto con el que hemos venido trabajando:
fecha_hora <- as.POSIXlt(fecha_hora) # Definimos formato
unlist(fecha_hora) # Sacamos de la lista los objetos almacenados
## sec min hour
## "32.0195159912109" "59" "16"
## mday mon year
## "28" "2" "118"
## wday yday isdst
## "3" "86" "0"
## zone gmtoff
## "CST" "-21600"
¿Qué es lo que tenemos? Se nos muestran los componentes de la lista, que está representada por el número de segundos, minutos, hora (en formato de 24 horas). Luego viene mday que es el día del mes (inicia en 1), mon es el mes del año (comienza en enero = 0), year que representa el año (inicia en 0 = 1900),wday el día de la semana (inicia domingo = 0), yday es el número de día del año (1 de enero = 0). Por último la variable isdst lo que hace es indicarnos si un horario de verano está siendo considerado (0 = FALSE, como en este caso)
Cuando traemos datos a R, en ocasiones hay que hacerle explícito a R el tipo de datos que tenemos. En el caso de las fechas es necesario, ya que de esta forma determinamos qué dato corresponde a cuál componente de lo que se considera una fecha u hora.
Vamos a utilizar el set de datos Flights that Depart NYC in 2013 que tiene como nombre nycflights13. Este se encuentra en el paquete con el mismo nombre. (Si no lo tienen, pueden instalarlo con la función install.packages("nycflights13"))
data <- nycflights13::flights # Guardar en objeto datos seleccionados
head(data)
## # A tibble: 6 x 19
## year month day dep_time sched_dep_time dep_delay arr_time
## <int> <int> <int> <int> <int> <dbl> <int>
## 1 2013 1 1 517 515 2 830
## 2 2013 1 1 533 529 4 850
## 3 2013 1 1 542 540 2 923
## 4 2013 1 1 544 545 -1 1004
## 5 2013 1 1 554 600 -6 812
## 6 2013 1 1 554 558 -4 740
## # ... with 12 more variables: sched_arr_time <int>, arr_delay <dbl>,
## # carrier <chr>, flight <int>, tailnum <chr>, origin <chr>, dest <chr>,
## # air_time <dbl>, distance <dbl>, hour <dbl>, minute <dbl>,
## # time_hour <dttm>
El conjunto de datos trae 3 columnas con el año, el mes y el día, los cuales vamos a unir y darle format de fecha:
library(tidyr) # Paquete del cual vamos a utilizar función
data <- unite(data, Date, year, month,day, sep = "/")
head(data)
## # A tibble: 6 x 17
## Date dep_time sched_dep_time dep_delay arr_time sched_arr_time
## <chr> <int> <int> <dbl> <int> <int>
## 1 2013/1/1 517 515 2 830 819
## 2 2013/1/1 533 529 4 850 830
## 3 2013/1/1 542 540 2 923 850
## 4 2013/1/1 544 545 -1 1004 1022
## 5 2013/1/1 554 600 -6 812 837
## 6 2013/1/1 554 558 -4 740 728
## # ... with 11 more variables: arr_delay <dbl>, carrier <chr>,
## # flight <int>, tailnum <chr>, origin <chr>, dest <chr>, air_time <dbl>,
## # distance <dbl>, hour <dbl>, minute <dbl>, time_hour <dttm>
Ya que tenemos dicha column creada, vamos a indicarle R qué de esos datos pertenece al año, al mes y al día:
Rdate <- strptime(as.character(data$Date), "%Y/%m/%d")
class(Rdate)
## [1] "POSIXlt" "POSIXt"
Ya tenemos el objeto creado con las fechas por lo que ahora podemos unirlo al set de datos:
data <- data.frame(Rdate,data)
head(data)
## Rdate Date dep_time sched_dep_time dep_delay arr_time
## 1 2013-01-01 2013/1/1 517 515 2 830
## 2 2013-01-01 2013/1/1 533 529 4 850
## 3 2013-01-01 2013/1/1 542 540 2 923
## 4 2013-01-01 2013/1/1 544 545 -1 1004
## 5 2013-01-01 2013/1/1 554 600 -6 812
## 6 2013-01-01 2013/1/1 554 558 -4 740
## sched_arr_time arr_delay carrier flight tailnum origin dest air_time
## 1 819 11 UA 1545 N14228 EWR IAH 227
## 2 830 20 UA 1714 N24211 LGA IAH 227
## 3 850 33 AA 1141 N619AA JFK MIA 160
## 4 1022 -18 B6 725 N804JB JFK BQN 183
## 5 837 -25 DL 461 N668DN LGA ATL 116
## 6 728 12 UA 1696 N39463 EWR ORD 150
## distance hour minute time_hour
## 1 1400 5 15 2013-01-01 05:00:00
## 2 1416 5 29 2013-01-01 05:00:00
## 3 1089 5 40 2013-01-01 05:00:00
## 4 1576 5 45 2013-01-01 05:00:00
## 5 762 6 0 2013-01-01 06:00:00
## 6 719 5 58 2013-01-01 05:00:00
La columna date contenía datos separados por un guión, en donde el primer dato pertenecía al año, el segundo al mes y el tercero al día, por ende le dijimos a R que leyera esos datos como año, mes y día separados por un /: ("%Y/%m/%d")
Estas son abreviaciones que dan a entender diferentes formas de leer el dato en R. La siguiente es una lista de todas las abreviaciones:
| Símbolo | Significado |
|---|---|
| %a | Nombre de la semana abreviado |
| %A | Nombre de la semana completo |
| %b | Nombre del mes abreviado |
| %B | Nombre del mes completo |
| %c | Hora y Fecha específica a la localidad |
| %d | Día del mes como un número decimal (01-31) |
| %H | Horas como decimales en reloj de 24 horas |
| %I | Horas como decimales en reloj de 12 horas |
| %j | Día del año como número decimal (0 - 366) |
| %m | Mes como un número decimal (0-11) |
| %M | Minutos como un número decimal (00 - 59) |
| %p | AM/PM indicador en la localidad |
| %S | Segundos como número decimal (00 - 61) |
| %U | Semana del año (00 - 53) usando el primer domingo como el día 1 de la semana 1 |
| %w | Día de la semana como un número decimal (0 - 6, Domingo es 0) |
| %W | Semana del año (00 - 53) usando el primer lunes como día 1 de la semana 1 |
| %x | Fecha, específica de la localidad |
| %X | Hora, específica de la localidad |
| %Y | Año con centenario |
| %y | Año sin centenario |
| %Z | Zona horaria como un vector de tipo caracter |
Ahora bien, hay ocasiones en que por ejemplo, tenemos el día como un número, pero nos serviría más tener el nombre del día. Para esto existe la función weekdays()
ejemplo <- data[1,1]
ejemplo
## [1] "2013-01-01 CST"
Tenemos el 1ero de enero del 2013, ahora queremos ver qué día es este con su nombre:
weekdays(ejemplo)
## [1] "martes"
Fechas pueden venir en diversas formas, tenemos que aprender cómo lidiar con estas y cómo hacerle explícito a R qué es qué. Para esto tenemos como ayuda la tabla presentada anteriormente.
Un ejemplo podría ser el siguiente:
otras_fechas <- c("2feb2016","18jun1990","7nov1995")
strptime(otras_fechas,"%d%b%Y")
## [1] "2016-02-02 CST" "1990-06-18 CST" "1995-11-07 CST"
: ¿Qué hicimos?
Leímos un conjunto de fechas que tenían un formato de día, seguido del nombre del mes y por último el año completo y se lo hicimos saber a R indicándole %día, luego %mes y por último %año.
Ahora cada vez que tengamos fechas, sabremos que debemos indicarle a R qué contienen esos datos y qué es qué guiándonos con la tabla provista.
Para tener la diferencia entre dos fechas, podemos echar mano de la función difftime(). Hay que tomar en cuenta que esta función nos devuelve un objeto de clase difftime
# Primero hacemos un objeto a clase difftime:
as.difftime(otras_fechas, "%d%b%Y")
## Time differences in days
## [1] -785 -10145 -8177
Hay cálculos que se pueden generar con fechas y horas, ya sea sumando a una fecha o a una hora un número (que representará segundos), también entre fechas/horas e inclusive la utilización de operadores lógicos.
# vamos a generar dos fechas:
fecha_1 <- as.POSIXlt("2018-01-01")
fecha_2 <- as.POSIXlt("2017-01-02")
fecha_1 + 100000
## [1] "2018-01-02 03:46:40 CST"
Esto nos suma cien mil segundos, lo cual agrega 3 horas, 46 minutos y 40 segundos a la fecha que habíamos creado.
fecha_1 - 100000
## [1] "2017-12-30 20:13:20 CST"
Vemos que nos devuelve al 30 de diciembre del 2017 a las 20 con 13 minutos y 20 segundos.
fecha_1 - fecha_2
## Time difference of 364 days
Nos dice que entre las fechas hay 364 días.
fecha_1 <= fecha_2
## [1] FALSE
Si decimos que fecha_1 es menor o igual que fecha_2 R nos dice que eso es falso.
Si tenemos una pregunta como: ¿Cuántos días han transcurrido desde el 1 de diciembre del 2017 al 2 de enero del 2018?
difftime("2018-01-02","2017-12-01")
## Time difference of 32 days
La respuesta es que han transcurrido 32 días.
En este caso, si tenemos horas en lugar de fechas, podemos hacer uso de la función as.difftime()
tiempo_1 <- as.difftime("12:00:00")
tiempo_2 <- as.difftime("17:20:00")
tiempo_2 - tiempo_1
## Time difference of 5.333333 hours
Nos dice cuál es la diferencia en horas. (Si te preguntas porqué 5.33 en lugar de 5 horas y 20 minutos, recuerde que la respuesta está dada en horas y 20 minutos corresponden a 0.333 horas)
Si queremos generar secuencias de fechas, años, meses, semanas etc, en R lo podemos realizar sin mucho problema:
seq(as.POSIXlt("2018-01-01"), as.POSIXlt("2018-01-10"), "1 day")
## [1] "2018-01-01 CST" "2018-01-02 CST" "2018-01-03 CST" "2018-01-04 CST"
## [5] "2018-01-05 CST" "2018-01-06 CST" "2018-01-07 CST" "2018-01-08 CST"
## [9] "2018-01-09 CST" "2018-01-10 CST"
¿Qué hicimos? La función seq() nos genera un vector con una serie de valores de una secuencia, en este caso le indicamos dos elementos de tipo POSIXlt que corresponden a fechas y un último argumento que indica 1 día. Es decir, que del 1ero de enero del 2018 al 10 de enero del 2018 queremos una secuencia entre esas fechas que aumente de 1 día en 1 día.
seq(as.POSIXlt("2018-01-01"), as.POSIXlt("2018-02-01"), "1 weeks")
## [1] "2018-01-01 CST" "2018-01-08 CST" "2018-01-15 CST" "2018-01-22 CST"
## [5] "2018-01-29 CST"
¿Qué hicimos? Nuevamente indicamos un par de fechas que delimitan el inicio y el final de la secuencia y por último le decimos que la secuencia aumente de semana en semana.
Esto lo podemos hacer de igual manera si queremos que la secuencia aumente por meses months o por años year
seq(as.POSIXlt("2018-01-01"),as.POSIXlt("2018-01-02"),8000)
## [1] "2018-01-01 00:00:00 CST" "2018-01-01 02:13:20 CST"
## [3] "2018-01-01 04:26:40 CST" "2018-01-01 06:40:00 CST"
## [5] "2018-01-01 08:53:20 CST" "2018-01-01 11:06:40 CST"
## [7] "2018-01-01 13:20:00 CST" "2018-01-01 15:33:20 CST"
## [9] "2018-01-01 17:46:40 CST" "2018-01-01 20:00:00 CST"
## [11] "2018-01-01 22:13:20 CST"
En este caso 8000 segundos nos genera un aumento de 2 horas, 13 minutos y 20 segundos en cada elemento de la secuencia.
seq(as.POSIXlt("2018-01-01"), by = "weeks", length = 7)
## [1] "2018-01-01 CST" "2018-01-08 CST" "2018-01-15 CST" "2018-01-22 CST"
## [5] "2018-01-29 CST" "2018-02-05 CST" "2018-02-12 CST"
¿Qué hicimos? Generamos nuevamente una secuencia en donde sólo le indicamos la fecha en la que debe de iniciar, seguido del argumento semanas y por último la cantidad de objetos que debería de crear. Es decir, el final está generado por la cantidad (length) de elementos que queremos en esa secuencia.
¡Listo! Si has llegado hasta este punto, felicidades. Espero que haya comprendido mejor cómo trabajar y manejar este tipo de dato para sus propios análisis.